package com.pai4j.user.service.relation;
import com.pai4j.common.enums.AccountRelationStatusEnum;
import com.pai4j.common.enums.AccountRelationTypeEnum;
import com.pai4j.common.enums.MessageTypeEnum;
import com.pai4j.common.enums.messagequeue.MessageBroadChannelEnum;
import com.pai4j.common.service.messagequeue.producer.MessageQueueProducer;
import com.pai4j.common.util.DateUtils;
import com.pai4j.common.util.JsonUtil;
import com.pai4j.common.util.RedisUtil;
import com.pai4j.domain.vo.dto.WaitConfirmRelationBaseDTO;
import com.pai4j.domain.vo.request.mess.MessageNotifyVO;
import com.pai4j.domain.vo.response.UserBaseResponseInfoVO;
import com.pai4j.domain.vo.response.WaitConfirmUserResponseVO;
import com.pai4j.user.repository.entity.AccountRelationEntity;
import com.pai4j.user.repository.dao.IFriendDAO;
import com.pai4j.user.repository.entity.FriendEntity;
import com.pai4j.user.service.UserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import com.pai4j.common.enums.RedisKeyEnum;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
@Service
public class User2UserAccountRelationService extends AbstractAccountRelationService {

    @Autowired
    private MessageQueueProducer messageQueueProducer;
    @Autowired
    private RedisUtil redisUtil;
    @Resource
    private UserService userService;
    @Autowired
    private IFriendDAO friendDAO;

    @Override
    protected AccountRelationTypeEnum getRelationType() {

        return AccountRelationTypeEnum.USER_USER;
    }

    @Override
    protected AccountRelationStatusEnum getSubscribeStatus() {
        // 用户订阅时，设置为等待确认状态
        return AccountRelationStatusEnum.WAIT_CONFIRMED;
    }

    @Override
    protected boolean isAsyncDoAfterComplete() {

        return true;
    }

    @Override
    protected void doAfterComplete(Long id, String sourceAccount, String targetAccount, boolean subscribe) {
        try {
            AccountRelationEntity relation = super.getRelationEntityById(id);
            if (subscribe) {
                if (relation != null && AccountRelationStatusEnum.CONFIRMED.getStatus().equals(relation.getStatus())) {
                    this.upsertFriend(sourceAccount, targetAccount, 1);
                } else if (relation != null && AccountRelationStatusEnum.WAIT_CONFIRMED.getStatus().equals(relation.getStatus())) {
                    this.doNotifyRedPointByWebSocket(sourceAccount, targetAccount);
                    this.addUser2WaitConfirmListCache(id, sourceAccount, targetAccount);
                }
            } else {
                this.upsertFriend(sourceAccount, targetAccount, 0);
                super.removeTargetAccountFromSourceLastChattingList(sourceAccount, targetAccount);
                super.removeTargetAccountFromSourceLastChattingList(targetAccount, sourceAccount);
            }
        } catch (Exception e) {
            log.error("User2User doAfterComplete error, id={}, source={}, target={}, subscribe={}", id, sourceAccount, targetAccount, subscribe, e);
            throw e;
        }
    }

    /**
     * 走redis查询用户待审核好友申请列表数据
     * 如果Redis缓存为空，会自动从数据库恢复
     *
     * @param account
     * @return
     */
    public List<WaitConfirmUserResponseVO> listWaitConformUsers(String account) {
        String cacheKey = RedisKeyEnum.WAIT_CONFIRM_USER_LIST_CACHE.getKey(account);
        // 按照申请时间倒序查询，一次拉去所有待审核（实际使用场景一般待审核不会很多，没必要分页查询）
        Set<String> waitConfirmUsers = redisUtil.zreverseRange(cacheKey, 0, Long.MAX_VALUE);
        
        // 【修复】如果Redis缓存为空，从数据库恢复待审核列表
        if (CollectionUtils.isEmpty(waitConfirmUsers)) {
           log.info("Redis缓存为空，从数据库恢复用户 {} 的待审核好友申请列表", account);
            restorePendingRelationsFromDatabase(account);
            // 重新从缓存读取
            waitConfirmUsers = redisUtil.zreverseRange(cacheKey, 0, Long.MAX_VALUE);
            if (CollectionUtils.isEmpty(waitConfirmUsers)) {
                return Collections.emptyList();
            }
        }
        
        List<WaitConfirmRelationBaseDTO> relations =
                waitConfirmUsers.stream()
                .map(cache -> JsonUtil.fromJson(cache, WaitConfirmRelationBaseDTO.class))
                .collect(Collectors.toList());
        List<String> sourceAccounts = relations.stream().map(WaitConfirmRelationBaseDTO::getSourceAccount).collect(Collectors.toList());
        // 走缓存批量查询待审核用户详情信息
        Map<String, UserBaseResponseInfoVO> waitConfirmUserMap = userService.batchGetUserInfoFromCache(sourceAccounts);
        return relations.stream().map(r ->
                new WaitConfirmUserResponseVO(r.getId(), waitConfirmUserMap.get(r.getSourceAccount())))
                .collect(Collectors.toList());
    }

    /**
     * 添加sourceAccount到targetAccount却确认redis缓存列表
     *
     * @param relationId 关系id，用于后续审核通过/拒绝
     * @param sourceAccount
     * @param targetAccount
     */
    private void addUser2WaitConfirmListCache(Long relationId, String sourceAccount, String targetAccount) {

        /**
         * 构造缓存value对象
         */
        WaitConfirmRelationBaseDTO cacheValueObj = this.buildWaitConfirmRelationBaseDTO(relationId, sourceAccount, targetAccount);

        /**
         * 这里我们完全依赖redis，暂不考虑缓存失效场景，redis使用永久有效
         */
        String cacheKey = RedisKeyEnum.WAIT_CONFIRM_USER_LIST_CACHE.getKey(targetAccount);
        redisUtil.zadd(cacheKey, JsonUtil.toJsonString(cacheValueObj), DateUtils.getCurrentTimeMillis(),
                          RedisKeyEnum.WAIT_CONFIRM_USER_LIST_CACHE.getExpireTime());
    }

    private WaitConfirmRelationBaseDTO buildWaitConfirmRelationBaseDTO(Long relationId, String sourceAccount, String targetAccount) {
        return  WaitConfirmRelationBaseDTO.builder()
                .id(relationId)
                .sourceAccount(sourceAccount)
                .targetAccount(targetAccount).build();

    }

    /**
     * 删除targetAccount待审核缓存中的sourceAccount信息
     *
     * @param relationId 关系id
     * @param sourceAccount
     * @param targetAccount
     */
    private void removeUser2WaitConfirmListCache(Long relationId, String sourceAccount, String targetAccount) {
        /**
         * 这里我们完全依赖redis，暂不考虑缓存失效场景，redis使用永久有效
         */
        WaitConfirmRelationBaseDTO cacheValueObj = this.buildWaitConfirmRelationBaseDTO(relationId, sourceAccount, targetAccount);
        String cacheKey = RedisKeyEnum.WAIT_CONFIRM_USER_LIST_CACHE.getKey(targetAccount);
        redisUtil.zremove(cacheKey, JsonUtil.toJsonString(cacheValueObj));
    }

    /**
     * 确认订阅关系（通过好友申请）
     * 备注为可选参数，如果提供备注则发送互动消息通知申请人
     *
     * @param confirmAccount 确认账号（审批人）
     * @param relationId 账号关系实体的自增id
     * @param remark 通过备注（可选，传null表示无备注）
     * @return
     */
    public Boolean confirmSubscribe(String confirmAccount, Long relationId, String remark) {
        AccountRelationEntity entity = super.getRelationEntityById(relationId);
        Assert.notNull(entity, "关系不存在");
        Assert.isTrue(entity.getTargetAccount().equals(confirmAccount), "无权限！");
        entity.setStatus(AccountRelationStatusEnum.CONFIRMED.getStatus());
        entity.setUpdateDate(new Date());
        // 更新关系
        super.updateAccountRelationEntity(entity);
        String sourceAccount = entity.getSourceAccount();
        String targetAccount = entity.getTargetAccount();
        
        // 【强一致性】同步更新统一待审核表状态
        try {
            super.pendingApprovalService.updateApprovalStatus(
                    com.pai4j.common.enums.PendingApprovalTypeEnum.FRIEND_REQUEST.getType(),
                    relationId,
                    com.pai4j.common.enums.GroupInvitationStatusEnum.ACCEPTED.getStatus(),
                    confirmAccount,
                    "好友申请已通过"
            );
            log.info("好友申请通过，统一待审核表已同步更新：relationId={}", relationId);
        } catch (Exception e) {
            log.error("同步更新统一待审核表失败，事务将回滚：relationId={}", relationId, e);
            throw new RuntimeException("同步更新统一待审核表失败", e);
        }
        
        // 添加群聊到用户账号关系列表
        super.init(targetAccount, sourceAccount);
        super.addTargetAccountRelationListCache(targetAccount, sourceAccount);
        super.init(sourceAccount, targetAccount);
        super.addTargetAccountRelationListCache(sourceAccount, targetAccount);
        // 同步好友表：互相关系生效，分别写入双方的friend记录
        this.upsertFriend(sourceAccount, targetAccount, 1);
        this.upsertFriend(targetAccount, sourceAccount, 1);
        // 好友申请通过，后续不需要在审核，删除redis缓存
        this.removeUser2WaitConfirmListCache(entity.getId(), sourceAccount, targetAccount);
        // 好友申请通过，刷新用户最新对话列表缓存(U1->U2, 将U2刷新到U1最新对话列表，同时需要将U1刷入到U2的最新对话列表中)
        super.addTargetAccount2SourceLastChattingList(sourceAccount, targetAccount);
        super.addTargetAccount2SourceLastChattingList(targetAccount, sourceAccount);
        
        // 如果有备注，发送互动消息通知申请人
        if (org.apache.commons.lang3.StringUtils.isNotBlank(remark)) {
            sendFriendApprovalNotification(confirmAccount, sourceAccount, true, remark);
        }
        
        return true;
    }
    
    /**
     * 确认订阅关系（通过好友申请，无备注）
     * 为了向后兼容，保留无备注的重载方法
     *
     * @param confirmAccount 确认账号
     * @param relationId 账号关系实体的自增id
     * @return
     */
    public Boolean confirmSubscribe(String confirmAccount, Long relationId) {
        return confirmSubscribe(confirmAccount, relationId, null);
    }

    /**
     * 拒绝订阅关系（拒绝好友申请）
     * 拒绝理由为可选参数，如果提供理由则发送互动消息通知申请人
     *
     * @param confirmAccount 拒绝账号（审批人）
     * @param relationId 确认关系ID
     * @param remark 拒绝理由（可选，传null表示无理由）
     * @return
     */
    public Boolean rejectSubscribe(String confirmAccount, Long relationId, String remark) {

        AccountRelationEntity entity = super.getRelationEntityById(relationId);
        Assert.notNull(entity, "关系不存在");
        Assert.isTrue(entity.getTargetAccount().equals(confirmAccount), "无权限！");
        entity.setStatus(AccountRelationStatusEnum.REJECTED.getStatus());
        entity.setUpdateDate(new Date());
        // 更新关系
        super.updateAccountRelationEntity(entity);
        
        // 【强一致性】同步更新统一待审核表状态
        try {
            super.pendingApprovalService.updateApprovalStatus(
                    com.pai4j.common.enums.PendingApprovalTypeEnum.FRIEND_REQUEST.getType(),
                    relationId,
                    com.pai4j.common.enums.GroupInvitationStatusEnum.REJECTED.getStatus(),
                    confirmAccount,
                    "好友申请已拒绝"
            );
            log.info("好友申请拒绝，统一待审核表已同步更新：relationId={}", relationId);
        } catch (Exception e) {
            log.error("同步更新统一待审核表失败，事务将回滚：relationId={}", relationId, e);
            throw new RuntimeException("同步更新统一待审核表失败", e);
        }
        
        // 好友申请被拒绝，删除redis缓存
        this.removeUser2WaitConfirmListCache(entity.getId(), entity.getSourceAccount(), entity.getTargetAccount());
        
        // 如果有理由，发送互动消息通知申请人
        if (org.apache.commons.lang3.StringUtils.isNotBlank(remark)) {
            sendFriendApprovalNotification(confirmAccount, entity.getSourceAccount(), false, remark);
        }
        
        return true;
    }
    
    /**
     * 拒绝订阅关系（拒绝好友申请，无理由）
     * 为了向后兼容，保留无理由的重载方法
     *
     * @param confirmAccount 拒绝账号
     * @param relationId 确认关系ID
     * @return
     */
    public Boolean rejectSubscribe(String confirmAccount, Long relationId) {
        return rejectSubscribe(confirmAccount, relationId, null);
    }

    /**
     * 从数据库恢复待审核好友申请列表到Redis缓存
     * 用于离线后重新上线时恢复待审核列表
     *
     * @param targetAccount 目标账号（待审核接收人）
     */
    private void restorePendingRelationsFromDatabase(String targetAccount) {
        try {
            // 从数据库查询所有待审核的好友申请
            List<AccountRelationEntity> pendingRelations = super.accountRelationDAO.findAllPendingRelations(
                    targetAccount,
                    AccountRelationTypeEnum.USER_USER.getType(),
                    AccountRelationStatusEnum.WAIT_CONFIRMED.getStatus()
            );
            
            if (CollectionUtils.isEmpty(pendingRelations)) {
                log.debug("用户 {} 没有待审核的好友申请", targetAccount);
                return;
            }
            
            // 重建Redis缓存
            for (AccountRelationEntity entity : pendingRelations) {
                addUser2WaitConfirmListCache(entity.getId(), entity.getSourceAccount(), entity.getTargetAccount());
            }
            
            log.info("用户 {} 的待审核好友申请列表已从数据库恢复到Redis，共 {} 条", targetAccount, pendingRelations.size());
        } catch (Exception e) {
            log.error("从数据库恢复用户 {} 的待审核好友申请列表失败", targetAccount, e);
        }
    }

    /**
     * 标记用户的待审核好友申请为已读
     * 同时更新数据库和清除Redis未读标记
     *
     * @param targetAccount 目标账号（待审核接收人）
     * @return 标记已读的记录数
     */
    public int markPendingRelationsAsRead(String targetAccount) {
        log.info("标记用户 {} 的待审核好友申请为已读", targetAccount);
        
        try {
            // 更新数据库中的isRead字段
            int updatedCount = super.accountRelationDAO.markPendingRelationsAsRead(
                    targetAccount,
                    AccountRelationTypeEnum.USER_USER.getType(),
                    AccountRelationStatusEnum.WAIT_CONFIRMED.getStatus()
            );
            
            log.info("用户 {} 的待审核好友申请已标记为已读，共 {} 条", targetAccount, updatedCount);
            return updatedCount;
        } catch (Exception e) {
            log.error("标记用户 {} 的待审核好友申请为已读失败", targetAccount, e);
            throw new RuntimeException("标记待审核好友申请为已读失败", e);
        }
    }

    /**
     * 查询用户未读的待审核好友申请数量
     *
     * @param targetAccount 目标账号（待审核接收人）
     * @return 未读待审核数量
     */
    public long countUnreadPendingRelations(String targetAccount) {
        try {
            long count = super.accountRelationDAO.countUnreadPendingRelations(
                    targetAccount,
                    AccountRelationTypeEnum.USER_USER.getType(),
                    AccountRelationStatusEnum.WAIT_CONFIRMED.getStatus()
            );
            log.debug("用户 {} 有 {} 条未读的待审核好友申请", targetAccount, count);
            return count;
        } catch (Exception e) {
            log.error("查询用户 {} 的未读待审核好友申请数量失败", targetAccount, e);
            return 0;
        }
    }

    @Override
    public Long getFollowersCount(String account) {
        try {
            return friendDAO.countByFriendIdAndStatus(account, 1);
        } catch (Exception e) {
            log.error("count followers by friend table error, account={}", account, e);
            return 0L;
        }
    }

    @Override
    public Long getFollowingCount(String account) {
        try {
            return friendDAO.countByUserIdAndStatus(account, 1);
        } catch (Exception e) {
            log.error("count following by friend table error, account={}", account, e);
            return 0L;
        }
    }

    private void upsertFriend(String userId, String friendId, int status) {
        FriendEntity fe = friendDAO.findAllByUserIdAndFriendId(userId, friendId);
        Date now = new Date();
        if (fe == null) {
            fe = new FriendEntity();
            fe.setUserId(userId);
            fe.setFriendId(friendId);
            fe.setApplyDate(now);
        }
        fe.setStatus(status);
        fe.setHandleDate(now);
        friendDAO.save(fe);
    }

    /**
     * 基于ws，为targetAccount推送订阅消息红点提醒
     *
     */
    private void doNotifyRedPointByWebSocket(String sourceAccount, String targetAccount) {
        MessageNotifyVO messageBase = new MessageNotifyVO();
        UserBaseResponseInfoVO sender = userService.getUserBaseInfoByUserId(sourceAccount);
        messageBase.setMessage("您有新的好友申请");
        messageBase.setSender(sender);
        messageBase.setSenderId(sourceAccount);
        messageBase.setReceiverId(targetAccount);
        messageBase.setType(MessageTypeEnum.APPLY.getType());
        messageQueueProducer.broadSend(MessageBroadChannelEnum.QUEUE_CHAT_NOTIFY, messageBase);
    }
    
    /**
     * 发送好友申请审批通知（互动消息）
     * 审批人处理好友申请后，如果有备注/理由，则通过消息队列发送互动消息通知申请人
     * 【架构优化】使用消息队列解耦，避免pai4j-user模块直接依赖pai4j-ugc模块
     * 
     * @param approverAccount 审批人账号
     * @param applicantAccount 申请人账号
     * @param approved true=通过，false=拒绝
     * @param remark 审批备注或拒绝理由
     */
    private void sendFriendApprovalNotification(String approverAccount, String applicantAccount, 
                                                boolean approved, String remark) {
        try {
            // 获取审批人信息
            UserBaseResponseInfoVO approver = userService.getUserInfoFromCache(approverAccount);
            String approverName = approver != null ? approver.getUserName() : "用户";
            
            // 构建消息内容
            String action = approved ? "通过" : "拒绝";
            String messageContent = String.format("您的好友申请已被%s", action);
            
            // 创建消息通知VO
            MessageNotifyVO messageNotify = new MessageNotifyVO();
            messageNotify.setSenderId(approverAccount);
            messageNotify.setReceiverId(applicantAccount);
            messageNotify.setSender(approver);
            messageNotify.setMessage(messageContent);
            messageNotify.setContent(remark); // 审批备注作为详细内容
            messageNotify.setType(MessageTypeEnum.INTERACTION.getType());
            messageNotify.setTime(System.currentTimeMillis());
            
            // 设置资源信息（好友信息）
            messageNotify.setResourceType("FRIEND");
            messageNotify.setResourceTitle(approverName);
            
            // 通过消息队列发送互动通知
            // 由pai4j-ugc或pai4j-connect模块监听QUEUE_INTERACTION_NOTIFY频道并持久化到数据库
            messageQueueProducer.broadSend(MessageBroadChannelEnum.QUEUE_INTERACTION_NOTIFY, messageNotify);
            
            log.info("发送好友申请审批通知成功（通过MQ）：审批人={}, 申请人={}, 结果={}, 备注={}",
                    approverAccount, applicantAccount, action, remark);
        } catch (Exception e) {
            log.error("发送好友申请审批通知失败：审批人={}, 申请人={}",
                    approverAccount, applicantAccount, e);
            // 互动消息发送失败不影响审批流程，只记录日志
        }
    }
}
